by: Francisco Ingham and Jeremy Howard. Inspired by Adrian Rosebrock
In this tutorial we will see how to easily create an image dataset through Google Images. Note: You will have to repeat these steps for any new category you want to Google (e.g once for dogs and once for cats).
from fastai.vision import *
import shutil
import os
import matplotlib
%matplotlib inline
matplotlib.rcParams['figure.figsize'] = [10, 10]
from zipfile import ZipFile
with ZipFile('data/images-20191220T222209Z-001.zip', 'r') as archive:
archive.extractall('data/')
Choose an appropriate name for your labeled images. You can run these steps multiple times to create different labels.
You will need to run this cell once per each category.
#Define function to grab classes from the file name itself
path = Path('./data/Belongs In a Museum')
folder = 'Belongs In a Museum'
#Get the class name
def get_class_from_name(path):
return [str(x).split("_")[0].split("/")[-1] for x in path.ls()]
class_series = pd.DataFrame(get_class_from_name(path), columns = ['classes'])
#Count the number of artifacts in each Country
count_artifact = class_series.groupby(['classes']).size().reset_index().sort_values([0], ascending = False)
count_artifact.columns = ['classes','count']
count_artifact
#Remove countries with less than 8 and with no country designation
remove_image_list = count_artifact.loc[(count_artifact['count'] < 8) | (count_artifact.classes == 'nan')].classes.values
remove_image_list
#Suggest removing files that have less than 8 and nan
#Folders must exist prior
path = './/data//Belongs In a Museum'
dest = './/data//Treasures'
files = os.listdir(path)
#Remove images that are in the remove list
for f in files:
if (f.startswith(tuple(remove_image_list))):
shutil.move(path + '//' + f, dest)
#Create a data bunch from the datablock api framework
func= lambda x: (str(x).split("_")[0].split("/")[-1])
path = Path('./data/Belongs In a Museum')
tfms = get_transforms(do_flip=False)
data = (ImageList.from_folder(path) #Where to find the data? -> in path and its subfolders
.split_by_rand_pct() #Split by random percentage, defaults to 20%
.label_from_func(func) #How to label? -> depending on the folder of the filenames
.transform(tfms, size=224) #Data augmentation? -> use tfms with a size of 64
.databunch()) #Finally? -> use the defaults for conversion to ImageDataBunch
Now you will need to download your images from their respective urls.
fast.ai has a function that allows you to do just that. You just have to specify the urls filename as well as the destination folder and this function will download and save all images that can be opened. If they have some problem in being opened, they will not be saved.
Let's download our images! Notice you can choose a maximum number of images to be downloaded. In this case we will not download all the urls.
You will need to run this line once for every category.
Then we can remove any images that can't be opened:
#View random selection of images
data.show_batch(rows=3, figsize=(7,8))
#Look at the classes, number of classes, # of images in training and test set
data.classes, data.c, len(data.train_ds), len(data.valid_ds)
#Set up the cnn and train with the data block and test set
learn = cnn_learner(data, models.resnet34, metrics=error_rate)
#Use the one-cycle method to train model
learn.fit_one_cycle(4)
#Save the weights
learn.save('stage-1')
#Unfreeze the entire network
learn.unfreeze()
#Run learning rate finder
learn.lr_find()
# If the plot is not showing try to give a start and end learning rate
# learn.lr_find(start_lr=1e-5, end_lr=1e-1)
learn.recorder.plot()
#Rule of thumb is to take the default end learning rate and divide by 5 or 10
#Since the lower learning rate remains horizontal, I took a larger learning rate.
learn.fit_one_cycle(4, max_lr=slice(1e-4,0.003/5))
#Saving weights from second model
learn.save('stage-2')
#Load the second model's weights
learn.load('stage-2');
#Create a interpretation object
interp = ClassificationInterpretation.from_learner(learn)
#Create confusion matrix
interp.plot_confusion_matrix()
#Plot greatest losses between predictions and actuals
interp.plot_top_losses(9, figsize=(15,11))
#The most confused pairs of artifacts
interp.most_confused(min_val=2)
#Export the cnn with the second set of weights
learn.export()
defaults.device = torch.device('cpu')
#Load the cnn model from the folder
path = Path('data/Belongs In a Museum')
learn = load_learner(path)
%matplotlib inline
#From the treasure folder which has images below 8 and nan predict their country.
path = Path('data/Treasures')
for i in path.ls():
plt.figure()
im = np.array(Image.open(i))
pred_class,pred_idx,outputs = learn.predict(open_image(i))
actual_class = (str(i).split("_")[0].split("/")[-1])
print("Predicted class", ":", str(pred_class).upper(), "/", "Actual Class", ":", str(actual_class).upper())
plt.imshow(im)
plt.show()
#Have the model guess the predicted class for my own pictures I took
from PIL import Image
%matplotlib inline
path = Path('data/PixelPhotos')
for i in path.ls():
im = open_image(i).rotate(-90)
pred_class,pred_idx,outputs = learn.predict(im)
actual_class = str(i).split("/")[-1].split(".")[0]
plt.show()
print("Predicted class", ":", str(pred_class).upper())
show_image(im)